Scrapbox Dynamic Macro
一部変更したかったので、わざわざコピペした
使い方
エンター区切りの同じ操作を2回やった後にctrl-mをすると、1回繰り返される
/icons/hr.icon
code:script.js
let cursor = $('#text-input')0; let enterPressed = false;
let eventStack = [];
let dmacro = null;
let macroRunning = false;
keydownのEventListener
ctrl + Aなどの複合的なカーソル操作を受け取る
code:script.js
$('#text-input').on('keydown', (e) => {
if (e.ctrlKey && e.key == 'm') {
dynamicMacro();
return;
}
// if (dmacro) {
// dmacro = null;
// stackEvent = [];
// }
if (e.key == 'Enter') enterPressed = true;
if (!e.key || specificKeys.includes(e.key)) return;
if (!operateKeys.includes(e.key) &&
!e.ctrlKey && !e.altKey &&
!e.shiftKey && !e.metaKey) return;
eventStack.push(JSON.stringify({
keyCode : e.keyCode,
ctrlKey : e.ctrlKey,
altKey : e.altKey,
shiftKey : e.shiftKey,
metaKey : e.metaKey
}));
});
文字列入力へのeventListener
半角入力はすべて受け取る
全角入力は確定時のみに一括で受け取る
code:script.js
$('#text-input').on('input', ({ originalEvent }) => {
// if (dmacro) {
// dmacro = null;
// stackEvent = [];
// }
if (originalEvent.inputType != 'insertText' && !enterPressed) {
enterPressed = false;
return;
}
if (originalEvent.data != 'getIndexByTitleLc' && !macroRunning) {
eventStack.push(originalEvent.data);
}
enterPressed = false;
});
code:script.js
const dynamicMacro = () => {
if (eventStack.length) {
dmacro = searchMacro();
} else {
execMacro(dmacro);
}
};
code:script.js
const searchMacro = () => {
let macroArray = [];
let candidate = [];
let originalStackLength = eventStack.length;
let latestEvent = eventStack.pop();
console.log(latestEvent);
let idx = eventStack.indexOf(latestEvent);
while (idx != -1) {
candidate.push(idx);
idx = eventStack.indexOf(latestEvent, idx + 1);
}
macroArray.unshift(latestEvent);
for(let i = 1; i < 30; i++) {
let previousCandidate = candidate.slice();
let newerEvent = eventStack.pop();
for (let j = 0; j < candidate.length; j++) {
if (candidatej+i == originalStackLength) { macroArray.unshift(newerEvent);
execMacro(macroArray);
return macroArray;
}
if (candidatej-i >= 0 && eventStack[candidatej-i] == newerEvent) continue; candidate.splice(j, 1);
j--;
}
console.log(candidate);
console.log(previousCandidate);
if (candidate.length == 0) {
if (previousCandidate.length == 1) {
if(previousCandidate0 + i + 1 == originalStackLength) { execMacro(macroArray);
console.log(123);
} else {
let remain = eventStack.slice(previousCandidate0+1); remain.push(newerEvent);
execMacro(remain);
console.log(456);
}
console.log(previousCandidate0-i+1); macroArray = eventStack.slice(previousCandidate0-i+1); macroArray.push(newerEvent);
eventStack = [];
return macroArray;
} else {
console.log(previousCandidate);
let notYetEnteredIndex = Math.max(...previousCandidate)+1;
execMacro(eventStack.slice(notYetEnteredIndex));
console.log(0 + ' ' + notYetEnteredIndex);
console.log(eventStack);
eventStack = [];
return macroArray;
}
} else {
macroArray.unshift(newerEvent);
}
}
eventStack = [];
return [];
}
引数の配列のevent (疑似objectと文字列) の発火を行う
code:script.js
const execMacro = (macro) => {
setTimeout(() => {
macroRunning = true;
for (let i = 0; i < macro.length; i++) {
if (e.startsWith('{') && e.endsWith('}')) { // KeyboardEvent
fireKeydown(e);
} else {// InputEvent
fireInsert(e);
}
}
macroRunning = false;
}, 50);
}
KeyboardEventの発火ができる
code:script.js
const fireKeydown = (event) => {
let { keyCode, ctrlKey, altKey, shiftKey, metaKey } = JSON.parse(event);
if (keyCode == 13) { // Enter
fireInsert('\n');
} else if (keyCode == 'Backspace') {
document.execCommand('forwardDelete', null, null);
} else {
let event = document.createEvent('Events');
event.initEvent('keydown', true, true);
event.keyCode = event.which = keyCode;
event.ctrlKey = ctrlKey;
event.altKey = altKey;
event.shiftKey = shiftKey;
event.metaKey = metaKey;
cursor.dispatchEvent(event);
}
}
InputEventの発火ができる
実際にはdocument.execCommandなので疑似的
code:script.js
const fireInsert = (str) => {
document.execCommand('insertText', null, str);
}
リファクタ